package com.segersten.kalaha

import java.util.Date;
import com.google.appengine.api.taskqueue.Queue
import com.google.appengine.api.taskqueue.QueueFactory
import com.google.appengine.api.taskqueue.TaskOptions.Method
import com.google.appengine.api.taskqueue.TaskOptions.Builder
import com.segersten.kalaha.BoardSession
import com.segersten.kalaha.PlayerCache
import com.segersten.kalaha.BoardSessionCache
import com.segersten.kalaha.domain.Event
import com.segersten.kalaha.domain.EventType

import java.util.logging.Logger

class LobbyController {

	private static Logger LOG = Logger.getLogger(getClass().getCanonicalName());

	def facebookKeys
	def kalahasettings

	def index = { render(text:"Welcome to LobbyController")}
	
	def getTicketByUserId = {
		SessionTicket ticket = SessionTicket.find("select from SessionTicket as s where s.ownerId = :p1", [p1:params.userId])
		if (ticket==null) {
			response.status = 404
			render(contentType:"text/json") {
					create = "http://${facebookKeys.callbackDomain}/tickets"
			}
			return
		} else {
			response.setHeader("Location", "http://${facebookKeys.callbackDomain}/tickets/${ticket.ticketId}")
			response.status = 301
			render(text:"")
			return
		}
	}

	def createTicket = {
		PlayerCache pc = new PlayerCache()
		Player player = pc.getBySessionId(params.clientSessionId)
		if (!player) {
			response.status = 404
			render(text:"Error: lobby - No player for ${params.clientSessionId}")
			return
		}

		SessionTicket ticket = getTicket(player.userId)

		if (!ticket) {
			def ticketId = createTicketIdFromUserId(player.userId)
			ticket = new SessionTicket(ticketId:ticketId,
					ownerId:player.userId,
					created:new Date(),
					lastUpdated:new Date(),
					sessionId:null,
					joinedSession:null,
					acceptBotOpponnent:true,
					ownerIsBot:false)

			SessionTicket opponentTicket = getRandomOpponentTicket(player.userId)
			if (opponentTicket!=null) {
				try {
					connectTickets(ticket, opponentTicket)
				} catch (Exception e) {
					LOG.info("Failed to connect players ${e.getMessage()} - ${e.getClass().getCanonicalName()}")
					response.status = 500 // todo 1find correct response code so that client can create a ticket
					// todo 2 clean up ticket classes
					render(text:"Failed") // todod return REST xml
				}
			} else {
				ticket.save()
				LOG.info("Created ticket for ${player.name}")
			}
		}

		response.status = 201
		renderTicket2Json(ticket)
	}
	
	def updateTicket =  {
		if (params.ticketId==null) {
			response.status = 404
			render(text: "No params")
			return
		}
		
		PlayerCache pc = new PlayerCache()
		Player player = pc.getBySessionId(params.clientSessionId)
		if (!player) {
			response.status = 401
			render(text:"Error: not authorized")
			return
		}

		SessionTicket ticket = SessionTicket.find("select from SessionTicket as s where s.ticketId = :p1", [p1:params.ticketId])
		if (ticket==null) {
			response.status = 404
			render(text: "Not found")
			return
		}
		
		ticket.lastUpdated = new Date()
		
		ticket.save()
		
		response.status = 200 // todo change to update successfull statuscode
		renderTicket2Json(ticket)
	}
	
	def deleteTicket =  {
		if (params.ticketId==null) {
			response.status = 404
			render(text: "No params")
			return
		}

		PlayerCache pc = new PlayerCache()
		Player player = pc.getBySessionId(params.clientSessionId)
		if (!player) {
			response.status = 401
			render(text:"Error: not authorized")
			return
		}

		SessionTicket ticket = SessionTicket.find("select from SessionTicket as s where s.ticketId = :p1", [p1:params.ticketId])
		if (ticket==null) {
			response.status = 404
			render(text: "Not found")
			return
		}

		if (ticket.sessionId!=null) {
			response.status = 400
			render(text:"Cannot delete ticket in ongoing game")
		}
		LOG.info("Remove ticket owned by ${ticket.ownerId}")
		CanceledSessionTicket canceledTicket = new CanceledSessionTicket(ticketId:ticket.ticketId,
				ownerId:ticket.ownerId,created:new Date(),
				reason:CanceledSessionTicket.CLIENT_REQUEST)
		canceledTicket.save()
		ticket.delete()
		response.status = 200 // todo change to update successfull statuscode
		render(text:"Canceled")
	}

	def getTicketById = {
		if (params.ticketId==null) {
			response.status = 404
			render(text: "No params")
			return
		}

		SessionTicket ticket = SessionTicket.find("select from SessionTicket as s where s.ticketId = :p1", [p1:params.ticketId])
		if (ticket==null) {
			response.status = 404
			render(text: "Not found")
			return
		}

		response.status = 200
		renderTicket2Json(ticket)
	}
	
	def renderTicket2Json = { def ticket ->
		render(contentType:"text/json") {
			ownerId = ticket.ownerId
			ticketId = ticket.ticketId
			created = ticket.created
			lastUpdated = ticket.lastUpdated
			acceptBots = ticket.acceptBotOpponnent
			self = "http://${facebookKeys.callbackDomain}/tickets/${ticket.ticketId}"
			if (ticket.sessionId==null) { 
				edit = "http://${facebookKeys.callbackDomain}/tickets/${ticket.ticketId}?"
			} else {
				game = "http://${facebookKeys.callbackDomain}/games/${ticket.sessionId}?"
			}
		}
	}

	def getAllTickets = {
		response.status = 200
		// TODO limit or paging
		def tickets = SessionTicket.list()
		PlayerCache pc = new PlayerCache()
		render(contentType:"text/json") {
			tickets = array {
				for(t in tickets) {
					ticket(ticketId:t.ticketId)
				}
			}
		}
	}

	def listTickets = {
		SessionTicket ticket = null
		render(text: "{id} / {ownerId} / {ticketId} / {created} / {sessionId} / {sessionId} / {joinedSession}<br>")
		if (params.id) {
			ticket = SessionTicket.get(params.id)
			if (ticket)
				render(text: "<hr>By id : ${ticket.id} / ${ticket.ownerId} / ${ticket.ticketId} / ${ticket.created}<br>")
			else
				render(text: "<hr>By id ${params.id} not found")
		}

		if (params.ownerId) {
			ticket = SessionTicket.findByOwnerId(params.ownerId)
			if (ticket)
				render(text: "<hr>By ownerId ${ticket.id} / ${ticket.ownerId} / ${ticket.ticketId} / ${ticket.created}<br>")
			else
				render(text: "<hr>${params.ownerId} not found")
		}

		if (params.ownerId) {
			ticket = SessionTicket.find("select from SessionTicket as s where s.ownerId = :p1", [p1:params.ownerId])
			if (ticket)
				render(text: "<hr>2 By ownerId HQL ${ticket.id} / ${ticket.ownerId} / ${ticket.ticketId} / ${ticket.created}<br>")
			else
				render(text: "<hr>2 HQL ${params.ownerId} not found")
		}
	}

	def listAllTickets = {
		def results = SessionTicket.list()
		render(text: "{id} / {ownerId} / {ticketId} / {created} / {lastUpdated} / {sessionId} / {joinedSession} / {game session}<br>")
		BoardSessionCache bsc = new BoardSessionCache()
		results.each { result ->
			String avail = bsc.get(result.sessionId) ? "Yes":"No"
			render(text: "${result.id} / ${result.ownerId} / ${result.ticketId} / ${result.created} / ${result.lastUpdated} / ${result.sessionId} / ${result.joinedSession} / ${avail}<br>")
		}
	}

	

	private Player getPlayerFromCache(String userId) {
		PlayerCache pc = new PlayerCache()
		return pc.getByUserId(userId)
	}

	private String createTicketIdFromUserId(String userId) {
		return userId
	}

	private SessionTicket getTicket(String userId) {
		return SessionTicket.find("select from SessionTicket as s where s.ownerId = :p1", [p1:userId])
	}

	private SessionTicket getRandomOpponentTicket(String userId) {
		def ticket = SessionTicket.find("select from SessionTicket as s where s.ownerId <> :p1 and s.sessionId = null", [p1:params.userId])
		return ticket
	}

	private void connectTickets(SessionTicket ticket, SessionTicket opponentTicket) {
		//  	try {
		if (ticket.sessionId || opponentTicket.sessionId) {
			//at least one already connected, just return
			String msg = "Already connected for ${ticket.ownerId} - ${ticket.sessionId} || ${opponentTicket.ownerId} - ${opponentTicket.sessionId}"
			LOG.info(msg)
			throw new IllegalStateException(msg)
		}

		// yippie opponent found, create a session and update ticket
		String gameSessionId = "${opponentTicket.hashCode()}${ticket.hashCode()}" + System.currentTimeMillis()
		ticket.sessionId = gameSessionId
		ticket.joinedSession = new Date()
		opponentTicket.sessionId = gameSessionId
		opponentTicket.joinedSession = new Date()
		LOG.info("Connected players! ${ticket.ownerId} - ${ticket.sessionId} || ${opponentTicket.ownerId} - ${opponentTicket.sessionId}")

		// Would like to take then in same transcation but is not allowed
		// TODO manual rollback
		//SessionTicket.withTransaction { status ->
		try {
			opponentTicket.save()
			ticket.save()
		} catch(Exception e) {
			LOG.info("Could not connect ticket due to OptimisticLockException (probably) " + e.getClass().getCanonicalName())
			throw e
			//	}
		}

		// Create a game session
		BoardSession boardSession = new BoardSession(
				roundDuration:Integer.parseInt(kalahasettings.roundDuration),
				sessionId:ticket.sessionId,
				player1:getPlayerFromCache(ticket.ownerId),
				player2:getPlayerFromCache(opponentTicket.ownerId),
				events:Event.createEventList())
		BoardSessionCache bsc = new BoardSessionCache()
		bsc.put(boardSession)
		boardSession.addEvent(EventType.GAME_CREATED,GameType.KALAHA)
		boardSession.addEvent(EventType.PLAYER_1_JOINED,["name":boardSession.player1.name,"id":boardSession.player1.userId])
		boardSession.addEvent(EventType.PLAYER_2_JOINED,["name":boardSession.player2.name,"id":boardSession.player2.userId])
		bsc.put(boardSession)

		LOG.info("Opponents connected ${ticket.ownerId} - ${opponentTicket.ownerId}")

		render(text:"OK")
		//  	} catch (Exception e) {
		//		LOG.severe("Failed to connect two tickets " + e.getMessage())
		//}
	}
	
}
